#include "JSONParser.h"


namespace JSON{

	JSON parseJList(std::string::iterator*, std::string*);
	JSON parseJObject(std::string::iterator*, std::string*);
	std::string parseString(std::string::iterator*, std::string*);
	std::string consumeNumeric(std::string::iterator*, std::string*);
	void consumeSpaces(std::string::iterator*, std::string*);
	bool isSpace(char);
	bool isFloat(std::string*);

	
bool isSpace(char c){
	return c == ' ' || c == '\n' || c == '\t' || c == '\r' ? true : false;
}


void consumeSpaces(std::string::iterator *it, std::string *strPtr){
	while((*it) != strPtr->end() && (isSpace(*(*it)) || *(*it) ==','|| *(*it) == ':'))
		(*it) ++;
}


std::string consumeNumeric(std::string::iterator *it, std::string *strPtr){
	std::string num = "";
	while((*it) != strPtr->end() && (isSpace(*(*it)) || *(*it) != ',') ){
		num += *(*it);
		(*it)++;
	}
	consumeSpaces(it, strPtr);
	return num;
}


JSON parseJList(std::string::iterator *it, std::string *strPtr){
	JSON list;
	std::string num;

	(*it)++;
	//mov to first element
	while((*it) != strPtr->end() && isSpace(*(*it)))
		(*it)++;
	switch(*(*it)){
	case '{':
		list.listType = Object;
		break;
	case '[':
		list.listType = List;
		break;
	case '\"':
		list.listType = String;
		break;
	default:
		list.listType = Numeric;
		break;
	}

	JSON jStr, jNum;
	jStr.type = String;

	while((*it) != strPtr->end() && *(*it) != ']'){
		consumeSpaces(it, strPtr);
		switch(list.listType){
		case String:
			jStr.str = parseString(it, strPtr);
			list.list.push_back(jStr);
			break;
		case List:
			list.list.push_back(parseJList(it, strPtr));
			break;
		case Object:
			list.list.push_back(parseJObject(it, strPtr));
			break;
		case Integer:
			num = consumeNumeric(it, strPtr);
			jNum.i = atoi(num.c_str());
			list.list.push_back(jNum);
			break;
		case Double:
			num = consumeNumeric(it, strPtr);
			jNum.d = atof(num.c_str());
			list.list.push_back(jNum);
			break;
		case Numeric:
			num = consumeNumeric(it, strPtr);
			if(isFloat(&num)){
				list.listType = Double;
				jNum.type = Double;
				jNum.d = atof(num.c_str());
				list.list.push_back(jNum);
			}
			else{
				list.listType = Integer;
				jNum.type = Integer;
				jNum.i = atoi(num.c_str());
				list.list.push_back(jNum);
			}
			break;
		}
	}
	if((*it) != strPtr->end())
		(*it)++;
	list.type = List;
	return list;
}


JSON parseJObject(std::string::iterator *it, std::string *strPtr){
	std::string key;
	JSON obj, add;

	(*it)++;
	while((*it) != strPtr->end() && *(*it) != '}'){
		consumeSpaces(it, strPtr);
		key = parseString(it, strPtr);
		consumeSpaces(it, strPtr);
		switch(*(*it)){
		case '\"':
			add = JSON();
			add.type = String;
			add.str = parseString(it, strPtr);
			obj.object.emplace(key, add);
			break;
		case '{':
			obj.object.emplace(key, parseJObject(it, strPtr));
			break;
		case '[':
			obj.object.emplace(key, parseJList(it, strPtr));
			break;
		default:
			std::string num = consumeNumeric(it, strPtr);
			add = JSON();
			if(isFloat(&num)){
				add.type = Double;
				add.d = atof(num.c_str());
				obj.object.emplace(key, add);
			}
			else{
				add.type = Integer;
				add.i = atoi(num.c_str());
				obj.object.emplace(key, add);
			}
			break;
		}
		consumeSpaces(it, strPtr);
	}
	if((*it) != strPtr->end())
		(*it)++;
	obj.type = Object;
	return obj;
}


std::string parseString(std::string::iterator *it, std::string *strPtr){
	std::string str = "";
	(*it)++;
	while((*it) != strPtr->end() && *(*it) != '\"'){
		str += *(*it);
		(*it)++;
	}
	if((*it) != strPtr->end())
		(*it)++;
	consumeSpaces(it, strPtr);
	return str;
}


JSON parseJSONStr(std::string *strPtr){
	std::string::iterator it;
	it = strPtr->begin();
	return *it == '[' ? parseJList(&it, strPtr) : parseJObject(&it, strPtr);
}


bool isFloat(std::string *strPtr){
	std::string::iterator it;

	for(it = strPtr->begin(); it != strPtr->end(); it++)
		if(*it == '.')
			return true;
	return false;
}

};